#include <iostream>
#include <type_traits>
#include <utility>

template <typename T>

T &&forward(typename std::remove_reference<T>::type &t) noexcept {
  std::cout << std::is_lvalue_reference<decltype(t)>::value << "\n";
  return static_cast<T &&>(t);
}

template <typename T>

T &&forward(typename std::remove_reference<T>::type &&t) noexcept {
  static_assert(!std::is_lvalue_reference<T>::value,
                "Can not forward an rvalue as lvalue.");
  std::cout << std::is_lvalue_reference<decltype(t)>::value << "\n";
  return static_cast<T &&>(t);
}

template <typename T, typename Func> void wrapper(T &&a, Func fn) {
  // forward lvalue to lvalues or rvalues
  fn(std::forward<T>(a));
}

struct Foo {
  Foo(int a1, int a2) : a(a1), b(a2), ret(0) {}
  int a, b, ret;
};

int main(int argc, char *argv[]) {
  int a = 0;
  forward<int>(a);    // forward lvalues to rvalues
  forward<int>(9527); // forward rvalues to rvalues

  Foo foo{1, 2};
  Foo &bar = foo;
  Foo &&baz = Foo(5, 6);

  wrapper(foo, [](Foo foo) {
    foo.ret = foo.a + foo.b;
    return foo.ret;
  });

  std::cout << foo.ret << "\n";

  wrapper(bar, [](Foo &foo) {
    foo.ret = foo.a - foo.b;
    return foo.ret;
  });
  std::cout << bar.ret << "\n";

  // move an rvalue to lvalue
  wrapper(std::move(baz), [](Foo &&foo) {
    foo.ret = foo.a * foo.b;
    return foo.ret;
  });

  std::cout << baz.ret << "\n";
  return 0;
}
